package net.helix.ui.panels.view;
import java.awt.GraphicsDevice;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.ArrayList;

import net.helix.Main;
import net.helix.core.transform.Transform;
import net.helix.core.transform.TransformDimension;
import net.helix.core.transform.ViewTransform;
import net.helix.element.Element;
import net.helix.element.Element_Camera;
import net.helix.element.ViewNodeElement;
import net.helix.engine.Engine;
import net.helix.engine.EngineTop;
import net.helix.engine.library.LibraryTreeNode;
import net.helix.engine.timeline.Timeline;
import net.helix.engine.timeline.layer.TimelineLayer;
import net.helix.engine.viewPath.ViewNode;
import net.helix.engine.viewPath.ViewPath;
import net.helix.graphics.GraphicsEngine;
import net.helix.graphics.GraphicsHelper;
import net.helix.ui.interactive.keyboard.KeyboardHelper;
import net.helix.ui.interactive.mouse.MouseHelper;
import net.helix.ui.panels.library.LibraryPathArrayTransferable;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.AWTGLCanvas;
import org.lwjgl.opengl.Drawable;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL21;
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.util.glu.GLU;
import org.lwjgl.util.glu.Sphere;
import org.lwjgl.util.vector.Vector4f;


public class ViewPreview extends AWTGLCanvas implements FocusListener, DropTargetListener{
	private static final long serialVersionUID = 1L;
	
	private ArrayList<Element> draggingItems = null;
	//private Transform draggingTransform = null;
	
	private Engine engine;
	
	private KeyboardHelper keyboardHelper;
	private MouseHelper mouseHelper;
	
	private float mouseX = 0;
	private float mouseY = 0;
	
	private float mouseLastX = 0;
	private float mouseLastY = 0;
	
	private ViewPath currentViewPath = null;
	public ViewTransform viewTransform = null;
	
	
	//public float viewNear = 1f;
	//public float viewFar = 1000f;
	
	public float mouseMinDepth = .1f;
	public float mouseMaxDepth = 5000f;
	
	public boolean hyperdraw = false;
	
	// These will exist in the Timeline class...
	//private TimelineLayer currentSelectedLayer = null;
	//private ArrayList<Element> currentSelectedElements = null;
	
	public Runnable runOnceFunction;
	
	public void setRunOnceFunction(Runnable toRun){
		runOnceFunction = toRun;
	}
	
	public Timeline getTimeline(){
		if(currentViewPath != null){
			return currentViewPath.getTimelineAtEnd();
		}else{
			return engine.getCurrentSelectedScene().timeline;
		}
	}
	
	public ViewPreview(Engine engine, GraphicsDevice gd, PixelFormat pf, Drawable drawable) throws LWJGLException {
		super(gd, pf, drawable);
		this.engine = engine;
		
		mouseHelper = new Mouse();
		keyboardHelper = new Keys();
		
		this.addComponentListener(this);
		this.addFocusListener(this);
		
		this.addMouseListener(mouseHelper);
		this.addMouseMotionListener(mouseHelper);
		
		this.addKeyListener(keyboardHelper);
		
		this.setFocusable(true);
		
		
		// TODO: make a drop target helper??
		this.setDropTarget(new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, this));
	}
	
	@Override
	public void initGL(){
		super.initGL();
		
		GraphicsEngine.initGL(this);
	}
	
	//boolean skip = true;
	boolean setResize = true;
	public void paintGL() {
		super.paintGL();
		
		// TODO: this should be a list...
		if(runOnceFunction != null){
			runOnceFunction.run();
			runOnceFunction = null;
		}
		
		GraphicsEngine.rebindRequired();
		
		
		
		try {
			float cRed = (float)((engine.getProjectProperties().bgColor >> 16) & 0xFF)/255f;
			float cGreen = (float)((engine.getProjectProperties().bgColor >> 8) & 0xFF)/255f;;
			float cBlue = (float)((engine.getProjectProperties().bgColor) & 0xFF)/255f;;
			
			GL11.glClearColor(cRed, cGreen, cBlue, 1);
			
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
			
			if(setResize){
				GL11.glViewport(0, 0, getWidth(), getHeight());
				setResize = false;
			}
			
			if(currentViewPath != null){
				renderViewPath(currentViewPath, true, true, true, true);
				
			}else{
				// the current viewpath is null.
				// that means render all the cameras...
				
				ArrayList<TimelineLayer> cameraLayers = engine.getListOfPossibleViews();
				
				int l = cameraLayers.size();
				for(int i=0;i<l;i++){
					
					Element element = cameraLayers.get(i).getAsElement(0);
					
					if(element instanceof Element_Camera){
						Element_Camera elementCamera = (Element_Camera) element;
						
						ViewPath thisPath = new ViewPath();
						thisPath.setRoot(new ViewNode((ViewNodeElement) element, 0));
						
						viewTransform = elementCamera.getViewTransform();
						
						renderViewPath(thisPath, false, false, true, false);
					}
				}
				
				// Make it show the border...
				GraphicsEngine.switchRootFlat(engine, viewTransform, null, this.getWidth(), this.getHeight(), false);
				
				GL11.glColor3f(0, 0, 0);
				GL11.glLineWidth(3f);
				
				GL11.glBegin(GL11.GL_LINE_STRIP);
				GL11.glVertex3f(-engine.getProjectProperties().width/2, -engine.getProjectProperties().height/2, 0);
				GL11.glVertex3f( engine.getProjectProperties().width/2, -engine.getProjectProperties().height/2, 0);
				GL11.glVertex3f( engine.getProjectProperties().width/2,  engine.getProjectProperties().height/2, 0);
				GL11.glVertex3f(-engine.getProjectProperties().width/2,  engine.getProjectProperties().height/2, 0);
				GL11.glVertex3f(-engine.getProjectProperties().width/2, -engine.getProjectProperties().height/2, 0);
				GL11.glEnd();
			}
			
			swapBuffers();
		} catch (LWJGLException e) {
			e.printStackTrace();
		}
		
		mouseLastX = mouseX;
		mouseLastY = mouseY;
	}
	Vector4f zoomInTo = null;
	Vector4f zoomInToMouse = null;
	Vector4f zoomInStartTranslate = null;
	Vector4f removeMe = null;
	
	Vector4f tiltTo = null;
	Vector4f tiltToMouse = null;
	Vector4f tiltStartDirection = null;
	
	Vector4f rotateCenter = null;
	Vector4f rotateToMouse = null;
	Vector4f rotateStartAngle = null;
	Vector4f rotateStartCamAngle = null;
	float rotateToSpeed = 1;
	float rotateStartDistance = 0;
	
	Sphere sphere = new Sphere();
	
	/**
	 * Lock on a specific type of control.
	 */
	ControlLock controlLock = ControlLock.NONE;
	enum ControlLock{
		NONE, ROTATE, DRAG, PAN, LOOK, ZOOM
	};
	
	private void renderViewPathInteractive(ViewPath path){
		// Interactivity
		
		Vector4f mousePos = GraphicsHelper.get3dMouse(mouseX, mouseY, .1f);
		
		float mouseDepth = GraphicsHelper.getMouseDepth(mouseX, mouseY);
		
		Vector4f mouseAtEnd = mousePos;
		
		if(viewTransform.getDimension() == TransformDimension.THREE){
			mouseAtEnd = GraphicsHelper.get3dMouse(mouseX, mouseY, mouseDepth);
			if(mouseDepth == 1f){
				// calculate line intersecting plane: y = 0. normal = (0, 1, 0)
				float dx = mouseAtEnd.x - mousePos.x;
				float dy = mouseAtEnd.y - mousePos.y;
				float dz = mouseAtEnd.z - mousePos.z;
				
				float x0 = mousePos.x;
				float y0 = mousePos.y;
				float z0 = mousePos.z;
				
				float t = mousePos.y / (dy);
				
				mouseAtEnd.x = x0 - dx*t;
				mouseAtEnd.y = y0 - dy*t;
				mouseAtEnd.z = z0 - dz*t;
				
				mouseDepth = GraphicsHelper.getMouseDepthAt(mouseAtEnd.x, mouseAtEnd.y, mouseAtEnd.z);
				
			}
			// Draw the 3d mouse position...
			if(mouseDepth > .01){
			
				GL11.glPushMatrix();
				
				GL11.glColor3f(.5f, .7f, .9f);
				
				GL11.glTranslatef(mouseAtEnd.x, mouseAtEnd.y, mouseAtEnd.z);
				
				sphere.draw(.1f, 10, 10);
				GL11.glPopMatrix();
			}
		}
		
		
		GL11.glColor3f(1, 0, 0);
		GL11.glPointSize(3);
		GL11.glBegin(GL11.GL_POINTS);
		GL11.glVertex3f(mousePos.x, mousePos.y, mousePos.z);
		GL11.glEnd();
		
		// the trail...
		/*
		GL11.glBegin(GL11.GL_LINES);
		GL11.glVertex3f(mousePos.x, mousePos.y, mousePos.z);
		GL11.glVertex3f(lastMousePos.x, lastMousePos.y, lastMousePos.z);
		GL11.glEnd();
		*/
		
		// TODO: selecting items??
		
		
		// TODO: dragging item
		// make this work with dragging groups of items.... (perhaps just rendering a bounding box is fine)
		
		if(draggingItems != null && viewTransform != null){ 
			
			
			for(int i=0;i<draggingItems.size();i++){
				Element dragItem = draggingItems.get(i);
				
				
				// render the item here
				if(viewTransform.getDimension() == TransformDimension.THREE){
					
					// 256 pixels = 1 unit on the grid
					// draggingTransform.setScale(1/256f, 1/256f, 1/256f);
					
					dragItem.getTransform().setTranslate(mouseAtEnd.x, mouseAtEnd.y, mouseAtEnd.z);
					
					//draggingTransform.setRotate(viewTransform.getRotX(), viewTransform.getRotY(), viewTransform.getRotZ());
				}else{
					dragItem.getTransform().setTranslate(mousePos.x, mousePos.y, 0);
				}
				
				dragItem.render();
				
			}
			
		}else{
			
			
			float clampedDepth = Math.max(Math.min(mouseDepth, 1f-(mouseMinDepth/viewTransform.getFar())), mouseMaxDepth/viewTransform.getFar());
			
			Vector4f newMouse = GraphicsHelper.get3dMouse(mouseLastX, mouseLastY, clampedDepth);
			Vector4f newMouseCompare = GraphicsHelper.get3dMouse(mouseX, mouseY, clampedDepth);
			
			//Vector4f mouseCloser = GraphicsHelper.get3dMouse(mouseX, mouseY, clampedDepth/2f);
			
			Vector4f mouseDiff = Vector4f.sub(newMouseCompare, newMouse, null);
			
			if(path.getDimension() == TransformDimension.TWO){
				
				if((controlLock == ControlLock.NONE || controlLock == ControlLock.ZOOM) && keyboardHelper.keys[17] == true && (mouseHelper.middleMouseDown || (mouseHelper.mouseDown && mouseHelper.rightMouseDown))){
					// TODO: improve this
					float scaleBy = 1f+(float)(mouseDiff.y)/50f;
					
					viewTransform.getTransform().scale(scaleBy, scaleBy, scaleBy);
					
					controlLock = ControlLock.ZOOM;
				}
				if((controlLock == ControlLock.NONE || controlLock == ControlLock.DRAG) && (mouseHelper.middleMouseDown || (mouseHelper.mouseDown && mouseHelper.rightMouseDown))){
					viewTransform.getTransform().translate(mouseDiff.x, mouseDiff.y, mouseDiff.z);
					
					controlLock = ControlLock.DRAG;
				}
				
				if(controlLock != ControlLock.NONE){
					this.repaint(16);
				}
			}else{
				
				//newMouseCompare.x *= viewTransform.getScaleX();
				//newMouseCompare.y *= viewTransform.getScaleY();
				//newMouseCompare.z *= viewTransform.getScaleZ();
				
				//mouseDiff.x *= viewTransform.getScaleX();
				//mouseDiff.y *= viewTransform.getScaleY();
				//mouseDiff.z *= viewTransform.getScaleZ();
				
				// 32 = space
				// 16 = shift
				// 17 = control
				
				
				if((controlLock == ControlLock.NONE || controlLock == ControlLock.ZOOM) && (mouseHelper.middleMouseDown || (mouseHelper.mouseDown && mouseHelper.rightMouseDown)) && keyboardHelper.keys[17]){
					// zoom in and out
					if(zoomInTo == null){
						zoomInTo = Vector4f.sub(new Vector4f(0,0,0,0), mouseAtEnd, null);
						zoomInToMouse = new Vector4f(mouseX, mouseY, clampedDepth, 0);
						zoomInStartTranslate = new Vector4f(viewTransform.getTransform().getX(), viewTransform.getTransform().getY(), viewTransform.getTransform().getZ(), 0);
						/*
						float dx = mouseCloser.x - newMouseCompare.x;
						float dy = mouseCloser.y - newMouseCompare.y;
						float dz = mouseCloser.z - newMouseCompare.z;
						
						// normalize
						float d = (float) Math.sqrt(dx*dx + dy*dy+ dz*dz);
						dx /= d;
						dy /= d;
						dz /= d;
						*/
						//removeMe = new Vector4f(mouseAtEnd.x, mouseAtEnd.y, mouseAtEnd.z, mouseAtEnd.w);
					}
					
					float dy = (zoomInToMouse.y - mouseY)/200f;
					
					Vector4f diff = Vector4f.sub(zoomInStartTranslate, zoomInTo, null);
					diff.x *= dy;
					diff.y *= dy;
					diff.z *= dy;
					
					Vector4f.add(zoomInStartTranslate, diff, diff);
					
					viewTransform.getTransform().setTranslate(diff.x, diff.y, diff.z);
					zoomInStartTranslate = new Vector4f(viewTransform.getTransform().getX(), viewTransform.getTransform().getY(), viewTransform.getTransform().getZ(), 0);
					zoomInToMouse = new Vector4f(mouseX, mouseY, clampedDepth, 0);
					
					controlLock = ControlLock.ZOOM;
					
				}else{
					zoomInTo = null;
					zoomInToMouse = null;
					zoomInStartTranslate = null;
				}
				
				if((controlLock == ControlLock.NONE || controlLock == ControlLock.LOOK) && (mouseHelper.middleMouseDown || (mouseHelper.mouseDown && mouseHelper.rightMouseDown)) && keyboardHelper.keys[32]){
					if(tiltTo == null){
						tiltTo = Vector4f.add(newMouseCompare, new Vector4f(), null);
						tiltToMouse = new Vector4f(mouseX, mouseY, clampedDepth, 0);
						tiltStartDirection = new Vector4f(viewTransform.getTransform().getRotX(), viewTransform.getTransform().getRotY(), viewTransform.getTransform().getRotZ(), 0);
						
					}
					float dx = (tiltToMouse.x - mouseX)/5f;
					float dy = (tiltToMouse.y - mouseY)/5f;
					
					viewTransform.getTransform().setRotate(dx + tiltStartDirection.x, dy + tiltStartDirection.y, 0);
					
					controlLock = ControlLock.LOOK;
					
				}else{
					tiltTo = null;
					tiltToMouse = null;
					tiltStartDirection = null;
				}
				
				if((controlLock == ControlLock.NONE || controlLock == ControlLock.DRAG) && (mouseHelper.middleMouseDown || (mouseHelper.mouseDown && mouseHelper.rightMouseDown)) && keyboardHelper.keys[16] == true){
					viewTransform.getTransform().translate(mouseDiff.x, mouseDiff.y, mouseDiff.z);
					
					controlLock = ControlLock.DRAG;
				}
				
				if((controlLock == ControlLock.NONE || controlLock == ControlLock.ROTATE) && (mouseHelper.middleMouseDown || (mouseHelper.mouseDown && mouseHelper.rightMouseDown))){
					if(rotateCenter == null){
						if(keyboardHelper.keys[67] == true){
							rotateCenter = Vector4f.sub(new Vector4f(), newMouseCompare, null);
							rotateToSpeed = 10f;
							
							
						}else{
							rotateCenter = Vector4f.sub(new Vector4f(0,0,0,0), GraphicsHelper.get3dMouse(getWidth()/2, getHeight()/2, clampedDepth), null);
							rotateToSpeed = 1f;
						}
						
						rotateToMouse = new Vector4f(mouseX, mouseY, clampedDepth, 0);
						
						// vector coming from rotation center out to beginning camera position...
						float dx = rotateCenter.x - viewTransform.getTransform().getX();
						float dy = rotateCenter.y - viewTransform.getTransform().getY();
						float dz = rotateCenter.z - viewTransform.getTransform().getZ();
						
						// normalize
						float d = (float) Math.sqrt(dx*dx + dy*dy+ dz*dz);
						dx /= d;
						dy /= d;
						dz /= d;
						
						rotateStartCamAngle = new Vector4f(viewTransform.getTransform().getRotX(), viewTransform.getTransform().getRotY(), viewTransform.getTransform().getRotZ(), 0);
						
						if(viewTransform.getTransform().getRotY() > 90f){
							dx = -dx;
							dz = -dz;
						}
						rotateStartAngle = new Vector4f(dx, dy, dz, 0);
						
						rotateStartDistance = d;
						
						
						
					}
					boolean flip = false;
					{
						float dx = (rotateToMouse.x - mouseX)/100f;
						float dy = (rotateToMouse.y - mouseY)/100f;
						
						float rdx = (float) Math.atan2(rotateStartAngle.z, rotateStartAngle.x);
						float rdy = (float) Math.acos(rotateStartAngle.y);
						
						if(rotateStartCamAngle.y > 90f){
							rdy = -rdy;
						}
						
						rdx -= dx;
						rdy += dy;
						
						
						
						while(rdy < -Math.PI){
							rdy += Math.PI*2;
						}
						while(rdy > Math.PI){
							rdy -= Math.PI*2;
						}
						
						if(rdy < 0f || rdy > Math.PI){
							flip = true;
						}
						
						
						float wy = rotateCenter.y - (float) (rotateStartDistance*Math.cos(rdy));
						
						float wx = rotateCenter.x - (float) (rotateStartDistance*Math.cos(rdx)*Math.sin(rdy));
						
						float wz = rotateCenter.z - (float) (rotateStartDistance*Math.sin(rdx)*Math.sin(rdy));
						
						viewTransform.getTransform().setTranslate(wx, wy, wz);
						//viewTransform.setRotate(rotateStartCamAngle.x - (float)(dx * 180/Math.PI), rotateStartCamAngle.y + (float)(dy * 180/Math.PI), rotateStartCamAngle.z);
					}
					
					
					{
						
						float dx = rotateCenter.x - viewTransform.getTransform().getX();
						float dy = rotateCenter.y - viewTransform.getTransform().getY();
						float dz = rotateCenter.z - viewTransform.getTransform().getZ();
						
						if(flip){
							dx = -dx;
							dz = -dz;
							//dy = -dy;
						}
						
						
						float rdx = (float) Math.atan2(dz, dx);
						float rdy = (float) Math.atan2(dy, Math.sqrt(dx*dx + dz*dz));
						// TODO: fix going upsidedown...
						
						float newrdx = (float)(rdx * 180/Math.PI) - 90f;
						float newrdy = (float)(rdy * 180/Math.PI);
						
						if(flip){
							newrdy = -(newrdy-90f)+90f;
						}
						
						
						if(rotateToSpeed > 1f){
							// easing motion toward the center...
							
							newrdx = viewTransform.getTransform().getRotX() - Main.subtractAngles(newrdx, viewTransform.getTransform().getRotX())/rotateToSpeed;
							newrdy = viewTransform.getTransform().getRotY() - Main.subtractAngles(newrdy, viewTransform.getTransform().getRotY())/rotateToSpeed;
							
							rotateToSpeed -= 1f;
						}else{
							rotateToSpeed = 1f;
						}
						viewTransform.getTransform().setRotate(newrdx, newrdy, viewTransform.getTransform().getRotZ());
						
						
					}
					
					controlLock = ControlLock.ROTATE;
				}else{
					rotateCenter = null;
					rotateStartAngle = null;
					rotateStartDistance = 0;
					rotateToSpeed = 1f;
				}
				
				if(controlLock != ControlLock.NONE){
					this.repaint(16);
					
				}
				
				
				
			}
			
			
		}
	}
	private void renderViewPath(ViewPath path, boolean director, boolean showCamera, boolean showGrid, boolean interactive){
		
		
		if(path.getDimension() == TransformDimension.TWO){
			GraphicsEngine.switchRootFlat(engine, viewTransform, path, getWidth(), getHeight(), director);
			
			path.render();
			
			if(interactive){
				renderViewPathInteractive(path);
			}
			
			
			if(showCamera){
				// the 2d camera....
				GL11.glColor3f(0, 0, 0);
				GL11.glLineWidth(3f);
				
				// TODO: make it look for the camera's position...
				// Look at the Element_Camera class, maybe it should render the camera from there.
				
				GL11.glBegin(GL11.GL_LINE_STRIP);
				GL11.glVertex3f(-engine.getProjectProperties().width/2, -engine.getProjectProperties().height/2, 0);
				GL11.glVertex3f( engine.getProjectProperties().width/2, -engine.getProjectProperties().height/2, 0);
				GL11.glVertex3f( engine.getProjectProperties().width/2,  engine.getProjectProperties().height/2, 0);
				GL11.glVertex3f(-engine.getProjectProperties().width/2,  engine.getProjectProperties().height/2, 0);
				GL11.glVertex3f(-engine.getProjectProperties().width/2, -engine.getProjectProperties().height/2, 0);
				GL11.glEnd();
				
				/*
				GL11.glBegin(GL11.GL_QUADS);
				GL11.glVertex3f(0, 0, 0);
				GL11.glVertex3f(engine.getProjectProperties().width, 0, 0);
				GL11.glVertex3f(engine.getProjectProperties().width, engine.getProjectProperties().height, 0);
				GL11.glVertex3f(0, engine.getProjectProperties().height, 0);
				GL11.glEnd();
				*/
			}
			
		}else{
			
			if(hyperdraw){
				/*
				// triple render pass for maximum depth precision and draw distance
				switchRoot3D(path, director, 50000f, 10000000f);
				draw3dGrid();
				path.render();
				GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
				
				switchRoot3D(path, director, 10f, 50000f);
				draw3dGrid();
				path.render();
				
				if(interactive){
					renderViewPathInteractive(path);
				}
				GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
				
				
				switchRoot3D(path, director, .001f, 10f);
				draw3dGrid();
				path.render();
				
				
				if(showCamera){
					// draw 3d camera
					
				}
				*/
				
				float saveDepthNear = viewTransform.getNear();
				float saveDepthFar = viewTransform.getFar();
				
				
				float skip = 100f;
				float d = 1000000f;
				for(int i=0;i<3;i++){
					// TODO: set depth d/skip, d
					viewTransform.setNear(d/skip);
					viewTransform.setFar(d);
					
					GraphicsEngine.switchRoot3D(engine, viewTransform, path, getWidth(), getHeight(), director);
					GraphicsHelper.draw3dGrid();
					path.render();
					
					
					GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
					d /= skip;
				}
				
				GL11.glColorMask(false, false, false, false);
				
				// 1f, 500000f
				viewTransform.setNear(1f);
				viewTransform.setFar(500000f);
				
				GraphicsEngine.switchRoot3D(engine, viewTransform, path, getWidth(), getHeight(), director);
				path.render();
				
				GL11.glColorMask(true, true, true, true);
				if(interactive){
					renderViewPathInteractive(path);
				}
				
				viewTransform.setNear(saveDepthNear);
				viewTransform.setFar(saveDepthFar);
			}else{
				// 1f, 10000f
				GraphicsEngine.switchRoot3D(engine, viewTransform, path, getWidth(), getHeight(), director);
				GraphicsHelper.draw3dGrid();
				path.render();
				
				if(interactive){
					renderViewPathInteractive(path);
				}
				
				if(showCamera){
					// draw 3d camera
					
				}
			}
			
		}
		
	}
	public void componentResized(ComponentEvent e) {
		setResize = true;
		
        this.repaint(16);
    }
	public void componentHidden(ComponentEvent e) {
    }
	public void componentShown(ComponentEvent e) {
		this.repaint(16);
    }
	
	 public void focusGained(FocusEvent e) {
		 engine.setCurrentSelectedView(this);
		 
		 this.repaint(16);
     }

    public void focusLost(FocusEvent e) {
    	this.repaint(16);
    }
	@Override
	public void dragEnter(DropTargetDragEvent arg0) {
	}
	@Override
	public void dragExit(DropTargetEvent arg0) {
		draggingItems = null;
		repaint(16);
	}
	@Override
	public void dragOver(DropTargetDragEvent dte) {
		mouseX = dte.getLocation().x;
		mouseY = dte.getLocation().y;
		Transferable transferable = dte.getTransferable();

		if(transferable != null){
			LibraryTreeNode[] paths = null;
			
			try {
				// Since serializing the transferable is pointlessly difficult and (mostly) useless, I'm using the static functions in EngineTop to do the work.
				if(transferable.getTransferData(LibraryPathArrayTransferable.LoadLibraryPathArrayTransferable) == Boolean.TRUE){
					
					paths = EngineTop.getLibraryTransferableData();
					if(paths != null){
						
						draggingItems = new ArrayList<Element>();
						
						for(int i=0;i<paths.length;i++){
							LibraryTreeNode node = paths[i];
							
							draggingItems.add(node.getItem().createElement());
							
							//draggingTransform.setScale(draggingItem.getWidth(), draggingItem.getHeight(), 1);
							
						}
					}
				}

			} catch (UnsupportedFlavorException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
		repaint(16);


	}
	@Override
	public void drop(DropTargetDropEvent dte) {
		Transferable transferable = dte.getTransferable();

		if(transferable != null){
			//LibraryTreeNode[] paths = null;


			try {

				if(transferable.getTransferData(LibraryPathArrayTransferable.LoadLibraryPathArrayTransferable) == Boolean.TRUE){

					//paths = EngineTop.getLibraryTransferableData();
					if(draggingItems != null){
						
						
						Timeline timeline = currentViewPath.getTimelineAtEnd();
						
						int l = draggingItems.size();
						for(int i=0;i<l;i++){
							// TODO: fix this for multiple items at once...
							
							//LibraryTreeNode node = paths[i];
							
							Element dragItem = draggingItems.get(i);
							
							//LibraryTreeNode _node = (LibraryTreeNode) node;
							
							if(currentViewPath == null) continue;
							
							if(timeline != null){
								if(timeline.getCurrentSelectedLayer() != null){
									timeline.getCurrentSelectedLayer().addElement(dragItem, currentViewPath.getFrameAtEnd());
								}
							}
							
							
							
							
							
							// TODO: make it place the object on the stage
							// TODO: if dragging more than 1 item from library...
							
						}
					}
					draggingItems = null;
				}
			} catch (UnsupportedFlavorException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		draggingItems = null;
		
		repaint(16);
	}
	
	@Override
	public void dropActionChanged(DropTargetDragEvent arg0) {
		
	}
	
	public void setViewPath(ViewPath path){
		this.currentViewPath = path;
	}
	public ViewPath getViewPath(){
		return currentViewPath;
	}
	class Mouse extends MouseHelper{
		
		@Override
		public void mouseClicked(MouseEvent e) {
			super.mouseClicked(e);
		}
		
		@Override
		public void mouseEntered(MouseEvent e) {
			super.mouseEntered(e);
		}
		
		@Override
		public void mouseExited(MouseEvent e) {
			super.mouseExited(e);
		}
		
		@Override
		public void mousePressed(MouseEvent e) {
			super.mousePressed(e);
			
			requestFocus();
			
			mouseX = e.getX();
			mouseY = e.getY();
			repaint(16);
		}
		
		@Override
		public void mouseReleased(MouseEvent e) {
			super.mouseReleased(e);
			
			controlLock = ControlLock.NONE;
			
			mouseX = e.getX();
			mouseY = e.getY();
			repaint(16);
		}
		
		@Override
		public void mouseDragged(MouseEvent e) {
			super.mouseDragged(e);
			
			mouseX = e.getX();
			mouseY = e.getY();
			repaint(16);
		}
		
		@Override
		public void mouseMoved(MouseEvent e) {
			super.mouseMoved(e);
			
			mouseX = e.getX();
			mouseY = e.getY();
			repaint(16);
			
	}
	}

	class Keys extends KeyboardHelper{
		@Override
		public void keyPressed(KeyEvent e) {
			super.keyPressed(e);
			
		}

		@Override
		public void keyReleased(KeyEvent e) {
			super.keyReleased(e);
			
		}

		@Override
		public void keyTyped(KeyEvent e) {
			if(e.getKeyChar() == 'h'){
				hyperdraw = !hyperdraw;
				System.out.println("Turning " + (hyperdraw?"on":"off") + " hyperdraw");
				
				repaint();
				
			}
			
			super.keyTyped(e);
			
		}
	}
}
